
;*******************************************************
;
;	SCSI Driver 'Read' filter.
;
;	Written by Matt Gulick.		Started June 13,1988
;
;	Copyright Apple Computer, Inc. 1988-90
;
;*******************************************************

;*******************************************************
;
;	This file contains the 'Read' filter as defined in
;	the ERS.
;
;*******************************************************

;*******************************************************
;
;	Revision History:
;
;*******************************************************

;	June 13,	1988	File started.
;	June 20,	1988	Registers in and out are

				STRING		PASCAL
				BLANKS		OFF
				PAGESIZE	70
				PRINT		NOGEN
				PRINT		NOMDIR
				MACHINE		M65816

				IMPORT		unit_state
				IMPORT		main_drvr
				IMPORT		call_type
				IMPORT		r_get_cache
				IMPORT		r_all_in_cache
				IMPORT		divend
				IMPORT		divsor
				IMPORT		result
				IMPORT		max_blk_cnt
				IMPORT		m_blk_size
				IMPORT		m_blk_cnt
				IMPORT		calc_bytes
				IMPORT		m_rslt
				IMPORT		gsos_dpage
				IMPORT		divide
				IMPORT		check_532_rw
				IMPORT		chk_play_mode
				IMPORT		auto_sense_data
				IMPORT		sense_data
				IMPORT		open_flag
				IMPORT		get_data_status
				IMPORT		internal_buff
				IMPORT		scratch0
				IMPORT		internal

				PRINT		OFF

				INCLUDE		'scsihd.equates'
				INCLUDE		'M16.MEMORY'
				INCLUDE		'M16.UTIL'
				PRINT		ON

				EJECT

				IF			block_dvc = true\
				AND			character_dvc = false		THEN
			
;*******************************************************
;
;	Main Entry point to the 'Read' filter.  This
;	"Filter" takes the information given by the caller
;	on direct page and builds the equivilent to a Read
;	Extended Status Call.  In order of appearence:
;
;			Verify that Device #		$0000
;			Call Number				=	$0002
;			Block Size				=	dib.blksize
;
;	We now Build the SCSI Main Driver Command and send
;	it.
;
;	The following will be validated by the Main Driver
;	when it builds the command. 
;
;			Request Count			=	Block Size * i
;			Block Number			=	Blk Num + Offset
;				This is for partitions.
;
;	After calling the Main driver and if no errors were
;	encountered, then the Transfer count will be
;	updated.
;
;	Inputs:		None.
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************

				ENDIF

				IF			character_dvc = true\
				AND			block_dvc = false		THEN
			
;*******************************************************
;
;	Main Entry point to the 'Read' filter.  This
;	"Filter" takes the information given by the caller
;	on direct page and builds the equivilent to a Read
;	Extended Status Call.  In order of appearence:
;
;			Verify that Device #		$0000
;			Call Number				=	$0002
;			Block Size				=	$0000
;			Device Opened			=	True
;
;	We now Build the SCSI Main Driver Command and send
;	it.
;
;	After calling the Main driver and if no errors were
;	encountered, then the Transfer count will be
;	updated.
;
;	Inputs:		None.
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************

				ENDIF

				EXPORT	Read
Read			PROC

;-------------------------------------------------------------------------------

				IF			block_dvc = true\
				AND			character_dvc = false		THEN

				stz		@cache
												;
												; Is the device Removable?
												;
				ldy		#dib.dvcchar

				lda		[dib_ptr],y
				and		#removable
				beq		@online					;No.
												;
												; This device is removable.  Now we
												; need to check to see if the unit
												; has gone offline, (then we need to
												; report that to the OS) or if the
												; unit has come back online (Rebuild
												; the DIBs).
												;
				jsr		unit_state
				bcs		@rts_out
												;
												; Is the device online?
												;
@online			ldy		#dib.dvcflag

				lda		[dib_ptr],y
				and		#dvc_hardofl
				bne		@off_line				;Yes.

				lda		[dib_ptr],y
				and		#dvc_online
				bne		@read					;Yes.
												;
												; Device is currently offline.
												;
@off_line		lda		#drvr_off_line
				sec
@rts_out		rts

												;
												; Preserve entry values.  This will
												; help restore the environment when
												; we exit.  To much is going on for
												; speeds sake to rely on these
												; locations.
												;
@read			lda		<rqst_cnt
				sta		@orig_rqst
				lda		<rqst_cnt+2
				sta		@orig_rqst+2
												;
												; Preserve Buffer pointer in case we
												; need to bump it.
												;
				lda		<buff_ptr
				sta		@orig_buff
				lda		<buff_ptr+2
				sta		@orig_buff+2
												;
												; Preserve starting Block Number in case
												; we need to bump it.
												;
				lda		<block_num
				sta		@orig_strt_blk
				lda		<block_num+2
				sta		@orig_strt_blk+2

;-------------------------------------------------------------------------------

				IF			scsi_dtype = apple_cd	THEN
												;
												; If the device is a CD_ROM device
												; then check to see if it is in audio
												; play mode.  If not clear the play
												; flag, otherwise set the audio play
												; flag.  This is used to prevent reads
												; while the audio tracks are playing.
												;
												; Is the device in Play Mode?
												;
				jsr		chk_play_mode

				ldy		#dib.dvcflag
				bcs		@no_play_mode

				lda		#play_mode
				ora		[dib_ptr],y
				bra		@store_play

@no_play_mode	lda		#play_mode--\
						$ffff
				and		[dib_ptr],y

@store_play		sta		[dib_ptr],y

				bcs		@read_2					;No.
												;
												; Device is Busy in PLAY MODE..
												;
				lda		#drvr_busy
				sec
				rts

@read_2
				ENDIF

;-------------------------------------------------------------------------------

												;
												; Let's check the request count.  If
												; this is $00000000, then exit clean
												; with no data transfered.
												;
				lda		<rqst_cnt
				and		#$01ff					;Cheap Check for multiple of 512
				bne		@bad_rqst_cnt
				ora		<rqst_cnt+2
				bne		@chk_max
				jmp		@out_of_here
												;
												; Block Number is beyond the range for this disk.
												;
@out_of_range	lda		#drvr_bad_blk
				sec
				rts
												;
												; Requested Byte Count not a multiple of 512
												;
@bad_rqst_cnt	lda		#drvr_bad_cnt
				sec
				rts
												;
												; Check to see if more is being
												; requested then we can give him.
												;
@chk_max		lda		<rqst_cnt
				sta		|divend
				lda		<rqst_cnt+2
				sta		|divend+2

				lda		#block_size
				sta		|divsor

				sec
				ldy		#dib.blkcnt
				lda		[dib_ptr],y
				sbc		<block_num
				sta		|max_blk_cnt
				tax

				ldy		#dib.blkcnt+2
				lda		[dib_ptr],y
				sbc		<block_num+2
				sta		|max_blk_cnt+2

				bcc		@out_of_range
				ora		|max_blk_cnt
				beq		@out_of_range

				jsr		divide
				bcc		@dont_fix_cnt

				lda		#block_size
				sta		|m_blk_size

				lda		|max_blk_cnt
				sta		|m_blk_cnt

				lda		|max_blk_cnt+2
				sta		|m_blk_cnt+2

				jsr		calc_bytes

				lda		|m_rslt
				sta		<rqst_cnt
				sta		@orig_rqst
				lda		|m_rslt+2
				sta		<rqst_cnt+2
				sta		@orig_rqst+2

@dont_fix_cnt

;-------------------------------------------------------------------------------

				IF			scsi_dtype = direct_acc	THEN

												;
												; Verify Block Size.
												;
@cnt_non_zero	ldy		#dib.blksize
				lda		[dib_ptr],y				;Block Size
				cmp		<blk_size
				bne		@chk_532

				ldy		#dib.blksize+2
				lda		[dib_ptr],y
				beq		@blk_size_ok

@bad_parm		lda		#drvr_bad_parm
				sec
@rts_now		rts
												;
												; Check for 532 byte block size
												;
@chk_532		tax

				lda		<blk_size
				cmp		#block_size
				bne		@bad_parm

				cpx		#$0214
				beq		@no_cache
				bra		@bad_parm

				ELSE

;-------------------------------------------------------------------------------

												;
												; Verify Block Size.
												;
@cnt_non_zero	ldy		#dib.blksize
				lda		[dib_ptr],y				;Block Size
				cmp		<blk_size
				bne		@bad_parm

				ldy		#dib.blksize+2
				lda		[dib_ptr],y
				beq		@blk_size_ok

@bad_parm		lda		#drvr_bad_parm
				sec
@rts_now		rts

				ENDIF

;-------------------------------------------------------------------------------

@blk_size_ok

;-------------------------------------------------------------------------------

				IF			cache_blks = true	THEN
												;
												; Skip Cache if FST Number >$8000
												; Otherwise check to see if they are
												; all in the cache.  If not we will
												; need to issue a read call.  If they
												; are all there we will skip the read
												; and go to the post processing
												; section.
												;
				bit		<fst_num				;Force Device Access?
				bmi		@no_cache				;Yes.

				dec		@cache

				jsr		r_all_in_cache			;Are they all there?
				bcc		@all_there				;Yes.

				ENDIF

;-------------------------------------------------------------------------------

@no_cache
												;
												; Build the (Read Data) Status
												; Command $8028, and just in case
												; it is not accepted, we will also
												; build $8008.
												;
				lda		<block_num				; Sent to me Low >> High.
				xba								; I Send it out High >> Low.
				sta		|c_block_num_l+2
				sta		|c_block_num_s
				lda		<block_num+2
				xba	
				sta		|c_block_num_l
												;
												; Set Main Driver Pointer to
												; our data for the command.
												;
				lda		#cmd_$8028
				sta		<scsi_mdrvr
				lda		#^cmd_$8028
				sta		<scsi_mdrvr+2
												;
												; Call Main Driver
												;
				lda		#scsit_stat
				sta		|call_type

;-------------------------------------------------------------------------------

				IF			scsi_dtype = direct_acc	THEN
												;
												; Issue the call.
												;
				jsr		check_532_rw
				bcs		@munge
@all_there		jmp		@completed
												;
												; At this point the extended call was not
												; excepted.  We must now issue the normal
												; version of this command.  First, we must
												; check to see if the request is within the
												; one byte block count range.  If not, we
												; will need to special process this request.
												;
												; Setup Divide routine while preserving the
												; origonal count for later.
												;
@munge			lda		@orig_rqst
				sta		|divend
				lda		@orig_rqst+2
				sta		|divend+2

				lda		<blk_size
				sta		|divsor

				jsr		divide
				bcs		@rts_0
												;
												; Get resulting block count.  If > a
												; single byte count, then we need to
												; break it into multiple calls.  If
												; not, then send it as is.
												;
				lda		|result
				sta		@blk_rqst
				lda		|result+1
				sta		@blk_rqst+1
				beq		@issue_call
												;
												; Set the max count for a single byte block
												; count (Block size * $100).
												;
				lda		<blk_size-1
				and		#$ff00
				sta		<rqst_cnt
				lda		<blk_size+1
				and		#$00ff
				sta		<rqst_cnt+2
												;
												; Set Main Driver Pointer to
												; our data for the command.
												;
@issue_call		lda		#cmd_$8008
				sta		<scsi_mdrvr
				lda		#^cmd_$8008
				sta		<scsi_mdrvr+2
												;
												; Call Main Driver
												;
				lda		#scsit_stat
				sta		|call_type
												;
												; Issue the call.
												;
				jsr		check_532_rw
				bcc		@did_munge
				sta		@error_loc
				lda		auto_sense_data+\
						rqst_sens.sense_key
				and		#$00ff					;Checking for $03
				cmp		#$0003					;This covers $03
				bne		@craped_out
												;
												; Some kind of I/O Error.
												;
@io_error		lda		#drvr_io
				sec
				rts

@craped_out		lda		@error_loc
				sec
@rts_0			rts

@error_loc		dc.w	null
												;
												; Check for special processing.
												;
@did_munge		dec		@blk_rqst+1
				bmi		@done					;All Done
				php
												;
												; Bump values for the next call.
												;
				clc
				lda		<rqst_cnt
				adc		<buff_ptr
				sta		<buff_ptr
				lda		<rqst_cnt+2
				adc		<buff_ptr+2
				sta		<buff_ptr+2
												;
												; Is this the last partial?
												;
				plp
				bne		@over					;Yes.
												;
												; Get remaining bytes to be transfered.
												;
				sec
				lda		@orig_rqst
				sbc		<rqst_cnt
				sta		<rqst_cnt
				lda		@orig_rqst+2
				sbc		<rqst_cnt+2
				sta		<rqst_cnt+2
				ora		<rqst_cnt
				beq		@done
												;
												;*****************************************
												;* Bump Block number.  Pay close         *
												;* attention to what this comment says.  *
												;* If you don't and you change the code  *
												;* then your on your own.  This word is  *
												;* in MSB >> LSB order.  We need to add  *
												;* $100 to it.  This is done by a simple *
												;* increment.                            *
												;*****************************************
												;
@over			inc		|c_block_num_s

				bra		@issue_call

@done

				ENDIF

;-------------------------------------------------------------------------------

				IF			scsi_dtype = apple_cd	THEN

				jsr		|main_drvr
				bcc		@all_there

				lda		auto_sense_data+\		;Is it an audio disk?
						rqst_sens.addnl_sens_code
				and		#$00ff					;Checking for $84, $64, or $63
				cmp		#$0063					;This cover $63
				beq		@audio_disk
				cmp		#$0064					;This cover $64
				beq		@audio_disk
				cmp		#$0084					;This cover $84
				beq		@audio_disk

				lda		#drvr_io
				sec
				rts								;There was an error!

@audio_disk		lda		#drvr_no_dev
				sec
				rts
@all_there

				ENDIF

;-------------------------------------------------------------------------------

@completed

;-------------------------------------------------------------------------------

				IF			cache_blks = true	THEN
												;
												; Check force device access Flag.  If
												; set, then exit out.
												;
				bit		@cache					;Forced Device Access?
				bpl		@out_of_here			;Yes.

												;
												; Check for CACHING.
												;
				clc								;Flag to force cache if needed.
				lda		<cache_prio				;Force Cache?
				bne		@do_cache				;Yes.
				sec								;Update only.
@do_cache		jsr		r_get_cache

				ENDIF

;-------------------------------------------------------------------------------

@out_of_here
												;
												; Restore the environment and
												; update Transfer Count.
												;
				ldx		|gsos_dpage
				lda		>rqst_cnt,x
				sta		<rqst_cnt
				lda		>rqst_cnt+2,x
				sta		<rqst_cnt+2

				lda		@orig_rqst
				sta		<trans_cnt
				lda		@orig_rqst+2
				sta		<trans_cnt+2

				lda		@orig_buff
				sta		<buff_ptr
				lda		@orig_buff+2
				sta		<buff_ptr+2

				lda		@orig_strt_blk
				sta		<block_num
				lda		@orig_strt_blk+2
				sta		<block_num+2
												;
												; Exit No Error.
												;
				lda		#$0000
				clc
@rts			rts

												;
												; Variables and storage for short call.
												;
@cache			dc.w	null					;Cache flag

@orig_buff		dc.l	null					;Origonal Buffer Pointer
@orig_rqst		dc.l	null					;Origonal Request Count
@orig_strt_blk	dc.l	null					;Origonal Starting Block Number
@blk_rqst		dc.l	null					;Number of Blocks requested
												;
												; Command Data for this call.
												;
cmd_$8028		dc.b	$28
				dc.b	$00
c_block_num_l	dc.l	$00000000
c_block_cnt_l	dcb.b	3,$00
				dcb.b	7,$00
												;
												; Command Data for this call.
												;
cmd_$8008		dc.b	$08
				dc.b	$00
c_block_num_s	dc.w	$0000
c_block_cnt_s	dc.b	$00
				dcb.b	7,$00

				ENDIF

;-------------------------------------------------------------------------------

				IF			character_dvc = true\
				AND			block_dvc = false		THEN

													;
													; Check to see if the Device is Opened.
													;
				lda		|open_flag
				beq		@not_open
													;
													; Is the device online?
													;
@open
				ldy		#dib.dvcflag

				lda		[dib_ptr],y
				and		#dvc_hardofl
				bne		@off_line					;Yes.

				lda		[dib_ptr],y
				and		#dvc_online
				bne		@read						;Yes.
													;
													; Device is currently offline.
													;
@off_line		lda		#drvr_off_line
				sec
@rts_out		rts
													;
													; Device is not Opened.
													;
@not_open		lda		#drvr_not_open
				sec
				rts

@read

;-------------------------------------------------------------------------------

				IF			scsi_dtype = character_dvc	THEN

												;
												; Save Requested amount for later.
												;
				lda		<rqst_cnt
				sta		@t_rqst_cnt				;Total Request Count (LOW)
				sta		@r_rqst_cnt				;Remaining Request Count (LOW)
				lda		<rqst_cnt+2
				sta		@t_rqst_cnt+2			;Total Request Count (HIGH)
				sta		@r_rqst_cnt+2			;Remaining Request Count (HIGH)

				lda		<buff_ptr
				sta		@orig_buffer
				lda		<buff_ptr+2
				sta		@orig_buffer+2

												;
												; Clear out Total Data Transfered Area.
												;
				stz		@t_transfered
				stz		@t_transfered+2
												;
												; Check to see if there is data to be had.
												;
@wait_loop		jsr		|get_data_status
				bcs		@io_error
				lda		|gds.data_avail\
						+sense_data
				ora		|gds.data_avail\
						+sense_data\
						+1
				beq		@wait_loop
				bra		@have_data
												;
												; Return an I/O Error.
												;
@io_error		lda		#drvr_io
				sec
				rts
												;
												; We have Data.  We need to check to see
												; if this is enough to give the user what
												; he requested.
												;
@have_data		stz		@enough_data

				lda		|gds.data_avail\		;Data is in High -> Low format.
						+sense_data\
						+1
				xba
				sta		|scratch0

				lda		|gds.data_avail\
						+sense_data\
						-1
				xba
				and		#$00ff
				sta		|scratch0+2

				sec
				lda		@r_rqst_cnt
				sbc		|scratch0
				sta		@stuff

				lda		@r_rqst_cnt+2
				sbc		|scratch0+2
												;
												; Was the request count < than what is
												; available in the device?  If so then
												; we will return request count bytes only.
												;
												; Were They equal?
												;
				blt		@do_rqst_cnt			;Yes.
				ora		@stuff
				beq		@do_rqst_cnt			;Yes.
												;
												; Well, it would apear that the request
												; count is > then what is available. We
												; will go ahead and get what is there
												; and then we will  check the wait flag.
												; If we are in wait state, then we will
												; loop around until we are able to get
												; what the user has requested. If we are
												; not in wait state, then we will exit
												; after the read.
												;
				ldx		|scratch0
				ldy		|scratch0+2

				sec
				lda		@r_rqst_cnt
				sbc		|scratch0
				sta		@r_rqst_cnt
				lda		@r_rqst_cnt+2
				sbc		|scratch0+2
				sta		@r_rqst_cnt+2

				dec		@enough_data
				bra		@do_read

@do_rqst_cnt	ldx		@r_rqst_cnt
				ldy		@r_rqst_cnt+2
				stz		@r_rqst_cnt
				stz		@r_rqst_cnt+2
												;
												; Set our Request Count for this call.
												;
@do_read		stx		<rqst_cnt
				sty		<rqst_cnt+2
												;
												; Set Main Driver Pointer to our data for the
												; command.
												;
				lda		#cmd_$8028
				sta		<scsi_mdrvr
				lda		#^cmd_$8028
				sta		<scsi_mdrvr+2
												;
												; It's a Status Call.
												;
				lda		#scsit_stat
				sta		|call_type
												;
												; Set Internal Command Flag
												;
				dec		|internal
												;
												; Issue the call.
												;
				jsr		|main_drvr
												;
												; Save transfer count.
												;
				clc
				ldy		#dib.trx_len
				lda		[dib_ptr],y
				adc		@t_transfered
				sta		@t_transfered
				ldy		#dib.trx_len+2
				lda		[dib_ptr],y
				adc		@t_transfered+2
				sta		@t_transfered+2
												;
												; Was there enough data?
												;
				lda		@enough_data
				beq		@no_wait				;Yes.
												;
												; Are we in WAIT Mode?
												;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#wait_mode
				beq		@no_wait				;No.
												;
												; Bump Buffer Pointer
												;
				clc
				ldy		#dib.trx_len
				lda		[dib_ptr],y
				adc		<buff_ptr
				sta		<buff_ptr
				ldy		#dib.trx_len+2
				lda		[dib_ptr],y
				adc		<buff_ptr+2
				sta		<buff_ptr+2
				
				jmp		@wait_loop				;Yes.
												;
												; Update transfer count.
												;
@no_wait		lda		@orig_buffer
				sta		<buff_ptr
				lda		@orig_buffer+2
				sta		<buff_ptr+2

				lda		@t_transfered
				sta		<trans_cnt
				lda		@t_transfered+2
				sta		<trans_cnt+2
												;
												; Clean Exit.
												;
				lda		#$0000
				clc
				rts
										
												; 
												; Data Area.
												;
@orig_buffer	dc.l	null					;Origonal Buffer Pointer
@t_rqst_cnt		dc.l	null					;TOTAL REQUEST COUNT
@r_rqst_cnt		dc.l	null					;REMAINING REQUEST COUNT
@t_transfered	dc.l	null					;AMOUNT ACTUALLY TRANSFERED
@enough_data	dc.w	null					;Is there enough data flag.
@stuff			dc.w	null					;Temporary Storage
												;
												; Command Description.
												;
cmd_$8028		dc.b	$28
rel_adr			dc.b	null					;Relative Address Bit is bit 0
t_data_type		dc.b	null					;Transfer Data Type
				dc.b	null					;Reserved
t_id			dc.w	null					;Transfer ID
t_length		dcb.b	3,null					;Transfer Length
control			dc.b	null					;Control Byte

				ENDIF

;-------------------------------------------------------------------------------

				ENDIF

;-------------------------------------------------------------------------------


				ENDP

				EJECT

				END
